package com.ynet.eq.aop.repeatedsubmit;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;

import com.ynet.eq.aop.syslog.entity.Log;
import com.ynet.eq.common.exception.ResponseCodeEnum;
import com.ynet.eq.common.exception.ResponseEntity;
import com.ynet.eq.redis.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @author 胡超雄
 * @date 2021/7/7 16:48
 * @desc 防止重复提交 切面处理类
 */
@Aspect
@Component
@Slf4j
@Order(1)   // 防止重复提交优先处理
public class NoRepeatSubmitAspect {

    @Resource
    private RedisUtil redisUtil;

    @Value("${system.name}") // mqtt/face/
    private String systemName;


    /**
     *  环绕通知
     * @param   joinPoint
     * @return  Object
     * @throws Throwable
     */
    @Around("cutMethod()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        final  String  PREFIX= "NoRepeatSubmit:" ;
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        NoRepeatSubmit annotation = method.getAnnotation(NoRepeatSubmit.class);
        //过期秒数
        int seconds = annotation.seconds();
        // 获取 ip ,方法, 参数,
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        String url = request.getRequestURL().toString();
        String methodName = request.getMethod();   //方法 get post ?
        String ipAddress = ServletUtil.getClientIP(request);

        // 获取ip
        // 请求的参数
        Object[] args = joinPoint.getArgs();
        String params = "";
        try {
            params = JSON.toJSONString(args);
            //  params
        } catch (Exception ignored) {
        }

        Log log = new Log();
        // 1 获取  ip   2  获取 post/get   3获取url  4获取参数
        // 1查询 redis 里面是否有这些参数  .存set.
        log.setIp(ipAddress);
        log.setMethodName(methodName);
        log.setRequestPath(url);
        log.setParams(params);
        Integer logHashCode = log.hashCode();
        //组装数据
        String  key =systemName+":"+PREFIX+logHashCode;
        Boolean isExist = redisUtil.hasKey(key);
        if(isExist){
            //如果存在返回
            ResponseCodeEnum tooMuch = ResponseCodeEnum.RESPONSE_TOO_MUCH;
            return ResponseEntity.response(tooMuch,"请勿重复请求");
        }
        redisUtil.set(key,"",seconds);
        return joinPoint.proceed();

    }


    /**
     * 定义切入点：对要拦截的方法进行定义与限制，如包、类
     * <p>
     * 1、execution(public * *(..)) 任意的公共方法
     * 2、execution（* set*（..）） 以set开头的所有的方法
     * 3、execution（* com.lingyejun.annotation.LoggerApply.*（..））com.lingyejun.annotation.LoggerApply这个类里的所有的方法
     * 4、execution（* com.lingyejun.annotation.*.*（..））com.lingyejun.annotation包下的所有的类的所有的方法
     * 5、execution（* com.lingyejun.annotation..*.*（..））com.lingyejun.annotation包及子包下所有的类的所有的方法
     * 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数，第一个参数为String类型，第二个参数为任意类型，第三个参数为Long类型的方法
     * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */
    @Pointcut("@annotation(com.ynet.eq.aop.repeatedsubmit.NoRepeatSubmit)")
    private void cutMethod() {

    }



}
